package io.github.wslxm.springbootplus2.gateway.aopfilter;


import io.github.wslxm.springbootplus2.constant.AkSkConst;
import io.github.wslxm.springbootplus2.core.enums.BaseDic;
import io.github.wslxm.springbootplus2.core.result.Result;
import io.github.wslxm.springbootplus2.core.result.ResultType;
import io.github.wslxm.springbootplus2.core.utils.date.XjLocalDateTimeUtil;
import io.github.wslxm.springbootplus2.manage.sys.dao.SysRoleAuthDao;
import io.github.wslxm.springbootplus2.manage.sys.model.vo.next.SysAuthVO;
import io.github.wslxm.springbootplus2.manage.sys.model.vo.next.SysUserAkSkAuthVO;
import io.github.wslxm.springbootplus2.manage.sys.service.SysAuthorityService;
import io.github.wslxm.springbootplus2.manage.sys.service.SysRoleService;
import io.github.wslxm.springbootplus2.manage.sys.service.SysUserService;
import io.github.wslxm.springbootplus2.properties.SecureProperties;
import io.github.wslxm.springbootplus2.starter.redis.util.RedisUtil;
import io.github.wslxm.springbootplus2.utils.JwtUtil;
import io.github.wslxm.springbootplus2.utils.XjSignMd5Util;
import io.github.wslxm.springbootplus2.utils.model.JwtUser;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * 接口验证/ 登录验证 / 权限验证
 *
 * @author wangsong
 */
@Component
@Slf4j
public class AuthFilter {

    @Autowired
    private HttpServletRequest request;
    @Autowired
    private HttpServletResponse response;
    @Autowired
    private SysAuthorityService sysAuthorityService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysRoleAuthDao sysRoleAuthDao;
    @Autowired
    private SysUserService sysUserService;

    /**
     * 安全配置，获取默认放行token
     */
    @Autowired
    private SecureProperties secureProperties;

    /**
     * 绝对放行接口，不受限于- 动态权限管理(勿随意配置)
     */
    private final List<String> URIS = new ArrayList<>();

    public AuthFilter() {
        // 管理端登录接口
        URIS.add("/api/admin/sys/login");
        URIS.add("/api/admin/sys/login/loginOut");
        URIS.add("/api/admin/sys/login/isLoginEnvironment");
    }


    /**
     * 登录授权认证
     * <>
     * token认证，授权认证，
     * 没有Token 直接放行, 让请求接入权限认证, 需要授权的接口没有token当然是认证不过的啦
     * 需要授权的接口, 在token 中获取当前登录用户的权限, 当前用户没有当前请求的接口权限当然也是认证不过的啦
     * // ===
     * 前端接口认证：暂无处理
     * 返回：
     * - 如果需要登录, 返回的jwt存储的用户信息, 用于记录日志, 如果接口不需要登录,返回的 null
     * - 如果登录过期或无接口权限,返回对应的错误信息，会直接返回到前端
     * </>
     */
    public Result<JwtUser> loginAuth(String methodUri) {
        // 绝对放行接口
        if (URIS.contains(methodUri)) {
            return Result.success(null);
        }
        SysAuthVO sysAuthVO = sysAuthorityService.findAuthByUri(request.getMethod(), methodUri);
        // 资源是否存在
        if (sysAuthVO == null) {
            log.error(methodUri + " " + ResultType.SYS_ERROR_404_NO_RESOURCES.getMsg());
            return Result.error(ResultType.SYS_ERROR_404_NO_RESOURCES);
        }
        // 接口是否禁用
        if (sysAuthVO.getDisable().equals(BaseDic.Whether.V1.getValue())) {
            return Result.error(ResultType.GATEWAY_DISABLE);
        }
        // 请求同TOKEN值当为 token 时直接放行
        String reqToken = request.getHeader(JwtUtil.TOKEN);
        if (secureProperties.getTokens() != null && secureProperties.getTokens().contains(reqToken)) {
            return Result.success(null);
        }
        // 4、登录/授权验证
        if (sysAuthVO.getState().equals(BaseDic.AuthorityState.V0.getValue())) {
            /**
             *  0- 无需登录 (不做任何处理)
             */
            return Result.success(null);
        } else if (sysAuthVO.getState().equals(BaseDic.AuthorityState.V1.getValue())) {
            /**
             *  1- 需登录 (先判断是否登录, 在判断是否有接口权限)
             */
            return authHandle(methodUri);
        }
        // 不做任何处理
        return Result.success(null);
    }


    /**
     * 授权验证
     *
     * @return boolean
     * @date 2025/9/22 10:32
     */
    private Result<JwtUser> authHandle(String methodUri) {
        // 判断是否是ak/sk 访问
        String accessKey = request.getHeader(AkSkConst.ACCESS_KEY);
        if (StringUtils.isNotBlank(accessKey)) {
            // sk 是否存在
            String secretKey = request.getHeader(AkSkConst.SECRET_KEY);
            if (StringUtils.isBlank(accessKey)) {
                return Result.error(ResultType.SYS_ERROR_403_AK_SK);
            }
            // ak/sk 数据验证
            SysUserAkSkAuthVO userByAkSk = sysUserService.findUserByAkSk(accessKey);
            if (userByAkSk == null || !XjSignMd5Util.md5(secretKey).equals(userByAkSk.getSecretKey())) {
                return Result.error(ResultType.SYS_ERROR_403_AK_SK);
            }
            // ak/sk 是否过期验证
            LocalDateTime expireTime = userByAkSk.getExpireTime();
            Long expireTimeMilli = XjLocalDateTimeUtil.parseTimestampMilli(expireTime);
            long currentTimeMillis = System.currentTimeMillis();
            if (expireTimeMilli < currentTimeMillis) {
                return Result.error(ResultType.SYS_ERROR_403_AK_SK_EXPIRED);
            }
            // 授权验证
            boolean isAuth = sysAuthorityService.isAuthByRoleCodesAndUri(userByAkSk.getRoleCodes(), request.getMethod(), methodUri);
            if (!isAuth) {
                // error
                return Result.error(ResultType.SYS_ERROR_403_NO_RESOURCES);
            }
            return Result.success(null);
        } else {
            // 登录用户调用 登录判断
            Result<JwtUser> result = JwtUtil.getJwtUserR(request, response);
            if (!result.getCode().equals(ResultType.SYS_SUCCESS.getValue())) {
                // error
                return result;
            }
            // 授权验证
            boolean isAuth = sysAuthorityService.isAuthByRoleCodesAndUri(result.getData().getRoleCodes(), request.getMethod(), methodUri);
            if (!isAuth) {
                // error
                return Result.error(ResultType.SYS_ERROR_403_NO_RESOURCES);
            }
            return Result.success(null);
        }
    }
}
